Skip to main content

Dockerizing dwd-api-fastapi

First API: dwd-api-fastapi (repo clone under repos/dwd-api-fastapi/).

Repo summary

ItemValue
FrameworkFastAPI
ASGI entrymain:app
Production serverGunicorn + uvicorn.workers.UvicornWorker
Configgunicorn.conf.py (already Azure-ready)
Python3.11 (runtime.txt)
Core depsrequirements.txt
ML extras (optional)requirements-ml.txt (tsai, wandb, PyTorch — large)
Health probeGET /health
Env template.env.example

The repo is already close to container-ready:

  • startup.sh runs gunicorn with the config file.
  • gunicorn.conf.py reads PORT (default 8080) and sends logs to stdout when PORT is set — ideal for Docker.
  • TSAI is optional; without requirements-ml.txt the app falls back to scikit-learn (same as Azure production).
SettingValue
<api-name>dwd-api-fastapi
Host port8001
Container port8080
Host bind127.0.0.1:8001:8080

Required environment variables

From .env.example — mount via /etc/dwd-api-fastapi/dwd-api-fastapi.env:

VariableRequiredPurpose
DWD_API_BASE_URLYes (unless clients always pass base_url)ASP.NET v2 results API root
OPENAI_API_KEYFor /agent onlyCopilot / MAF agent
OPENAI_CHAT_MODEL_IDNoDefault gpt-4o-mini
MCP_SERVER_URLNoMCP tools for agent
WANDB_*NoForecast experiment tracking

Image choice: slim vs ml

VariantInstallSizeForecast engineUse when
slimrequirements.txt only~500 MB order of magnitudescikit-learn fallbackProduction default; matches Azure
mlrequirements.txt + requirements-ml.txtSeveral GB (PyTorch)tsaiTSAI required in prod

Recommendation: Start with slim. Add an ml build target later if forecasts must use tsai on this server.

Step 1 — Add files to the git repo

Per-service build files are in the submodule repo root:

  • Dockerfile
  • .dockerignore

Stack orchestration (Compose, cross-service env, Nginx) lives in hydrotrek-dwd-suite:

Dockerfile templates: templates/docker/dwd-api-fastapi/ (Dockerfile, .dockerignore).

Local build verified: docker build --target slim -t dwd-api-fastapi:slim ./health returns 200 on 127.0.0.1:8001.

Step 2 — Build and test locally (Windows)

Single service (image only):

cd "c:\jacob\admin\GQC Computers\MSI Laptop 3\repos\dwd-api-fastapi"
docker build --target slim -t dwd-api-fastapi:slim .

Full stack (FastAPI + MCP + Copilot on dwd-stack):

cd "c:\jacob\admin\GQC Computers\MSI Laptop 3\repos\hydrotrek-dwd-suite"
git submodule update --init dwd-api-fastapi dwd-mcp-fastmcp dwd-copilot-server
cd docker
# Copy env/*.env.example → env/*.env and edit
docker compose up -d --build

Verify:

curl http://127.0.0.1:8001/health
curl http://127.0.0.1:8001/docs

Step 3 — Interim native deploy (optional)

If the server needs the API before Docker is installed, follow 02-adding-a-new-api.md with:

  • <api-name> = dwd-api-fastapi
  • <module>:<app> = main:app
  • Use gunicorn in the systemd unit (matches existing dwd-api.service in the repo)
ExecStart=/opt/venvs/dwd-api-fastapi/bin/gunicorn --config gunicorn.conf.py main:app

Set PORT=8001 and bind in gunicorn — or adjust gunicorn.conf.py bind for native 127.0.0.1:8001. The repo defaults to 0.0.0.0:8080; on the host use:

PORT=8001 GUNICORN_WORKERS=2

and ensure Nginx points at 127.0.0.1:8001.

Native and Docker can coexist during migration; only one should bind port 8001.

Step 4 — Docker on the Ubuntu server

When Docker is installed:

# One-time
sudo apt install docker.io docker-compose-v2

# Secrets (same as native path)
sudo mkdir -p /etc/dwd-api-fastapi
sudo cp dwd-api-fastapi.env /etc/dwd-api-fastapi/dwd-api-fastapi.env
sudo chmod 640 /etc/dwd-api-fastapi/dwd-api-fastapi.env
sudo chown root:apisvc /etc/dwd-api-fastapi/dwd-api-fastapi.env

# Compose file for production (stack — all three APIs)
sudo mkdir -p /etc/dwd-stack
sudo chown root:apisvc /etc/dwd-stack
sudo chmod 750 /etc/dwd-stack
sudo cp /opt/apis/hydrotrek-dwd-suite/docker/docker-compose.prod.yml /etc/dwd-stack/docker-compose.yml

# Or FastAPI-only (legacy): copy from hydrotrek-dwd-suite/docker and trim to one service

# systemd (stack example)
# ExecStart=/usr/bin/docker compose -f /etc/dwd-stack/docker-compose.yml up -d --remove-orphans

Production compose pulls a tagged image (when CI exists) or builds from /opt/apis/dwd-api-fastapi initially.

Step 5 — Nginx

Unchanged from 04-nginx-reverse-proxy.md. Upstream remains:

server 127.0.0.1:8001;

Repo hygiene before first image build

The local clone may contain wandb/ run artifacts. They are gitignored but would bloat the build context. The template .dockerignore excludes them. Remove from disk if present:

Remove-Item -Recurse -Force wandb -ErrorAction SilentlyContinue

Differences from existing dwd-api.service in repo

The repo’s bundled dwd-api.service targets /opt/dwd-api-fastapi with user www-data. Our server standard uses:

Repo fileServer standard
/opt/dwd-api-fastapi/opt/apis/dwd-api-fastapi (native) or image-only (Docker)
www-dataapisvc (native) or Docker (container user)
File logs under /var/log/gunicornstdout in Docker; journald on host

Do not copy the repo’s dwd-api.service verbatim — use templates/api.service.template or templates/docker/api.service.docker.template.

Next actions

  1. Review template Dockerfile and adjust worker counts for laptop hardware.
  2. Commit Docker files to the dwd-api-fastapi git repository.
  3. Test slim image locally with a real DWD_API_BASE_URL.
  4. Decide native-first vs wait for Docker on server.
  5. When ready, we can add a GitHub Actions workflow to build and push to GHCR/ACR.